<script setup lang="ts">
import {ref} from 'vue'
import type {UploadChangeParam, UploadProps} from 'ant-design-vue'
import {message, Upload} from 'ant-design-vue'
import {LoadingOutlined, PlusOutlined} from '@ant-design/icons-vue'
import {ossApi} from '@/api/modules/oss'
import type {OssTokenVO, UploadResult} from '@/types/oss'

const props = defineProps<{
  value?: string
  dir: string
  maxSize?: number // 最大文件大小，单位MB
  accept?: string  // 接受的文件类型
  disabled?: boolean
}>()

const emit = defineEmits<{
  (e: 'update:value', value: string): void
  (e: 'success', result: UploadResult): void
  (e: 'error', error: any): void
}>()

const loading = ref(false)
const ossData = ref<OssTokenVO>()
const fileList = ref<any[]>([])

// 获取上传凭证
const getOssToken = async () => {
  try {
    ossData.value = await ossApi.getToken(props.dir)
  } catch (error) {
    console.error('获取上传凭证失败:', error)
    message.error('获取上传凭证失败')
  }
}

// 生成唯一文件名
const generateFileName = (file: File) => {
  const ext = file.name.substring(file.name.lastIndexOf('.'))
  const timestamp = new Date().getTime()
  const random = Math.floor(Math.random() * 1000)
  return `${timestamp}_${random}${ext}`
}

// 上传前检查
const beforeUpload: UploadProps['beforeUpload'] = async (file) => {
  // 检查文件大小
  if (props.maxSize && file.size > props.maxSize * 1024 * 1024) {
    message.error(`文件大小不能超过 ${props.maxSize}MB`)
    return false
  }

  // 获取上传凭证
  await getOssToken()
  loading.value = true
  return true
}

// 自定义上传
const customRequest: UploadProps['customRequest'] = async ({ file, onSuccess, onError }) => {
  if (!ossData.value) {
    message.error('获取上传凭证失败')
    return
  }

  try {
    const fileName = generateFileName(file as File)
    const key = `${ossData.value.dir}${fileName}`

    // 创建一个隐藏的表单
    const formElement = document.createElement('form')
    formElement.style.display = 'none'
    formElement.method = 'POST'
    formElement.action = ossData.value.host
    formElement.enctype = 'multipart/form-data'
    formElement.target = 'uploadIframe'

    // 创建文件输入框
    const fileInput = document.createElement('input')
    fileInput.type = 'file'
    fileInput.name = 'file'
    fileInput.style.display = 'none'
    document.body.appendChild(fileInput)

    // 创建 FileList 对象
    const dataTransfer = new DataTransfer()
    dataTransfer.items.add(file as File)
    fileInput.files = dataTransfer.files

    // 添加其他表单字段
    const fields = {
      key,
      policy: ossData.value.policy,
      OSSAccessKeyId: ossData.value.accessKeyId,
      success_action_status: '200',
      signature: ossData.value.signature,
      'x-oss-security-token': ossData.value.securityToken
    }

    // 创建表单字段
    Object.entries(fields).forEach(([name, value]) => {
      const input = document.createElement('input')
      input.type = 'hidden'
      input.name = name
      input.value = value as string
      formElement.appendChild(input)
    })

    // 将文件输入框添加到表单
    formElement.appendChild(fileInput)

    // 创建用于接收响应的 iframe
    const iframe = document.createElement('iframe')
    iframe.name = 'uploadIframe'
    iframe.style.display = 'none'
    document.body.appendChild(iframe)

    // 监听 iframe 加载完成事件
    iframe.onload = () => {
      try {
        const result: UploadResult = {
          url: `${ossData.value!.host}/${key}`,
          name: fileName,
          path: key
        }
        onSuccess?.(result)
        emit('success', result)
        emit('update:value', result.url)
      } catch (error) {
        onError?.(error)
        emit('error', error)
        message.error('上传失败')
      } finally {
        // 清理 DOM
        document.body.removeChild(iframe)
        document.body.removeChild(formElement)
        document.body.removeChild(fileInput)
        loading.value = false
      }
    }

    // 提交表单
    document.body.appendChild(formElement)
    formElement.submit()
  } catch (error) {
    console.error('上传失败:', error)
    onError?.(error)
    emit('error', error)
    message.error('上传失败')
    loading.value = false
  }
}

// 处理上传状态变化
const handleChange = (info: UploadChangeParam) => {
  if (info.file.status === 'uploading') {
    loading.value = true
  } else {
    loading.value = false
  }
}
</script>

<template>
  <Upload
    v-model:file-list="fileList"
    :show-upload-list="false"
    :before-upload="beforeUpload"
    :custom-request="customRequest"
    :accept="accept"
    :disabled="disabled"
    @change="handleChange"
  >
    <div class="upload-wrapper">
      <img v-if="value" :src="value" alt="avatar" class="avatar" />
      <div v-else class="upload-placeholder">
        <LoadingOutlined v-if="loading" />
        <PlusOutlined v-else />
        <div class="upload-text">点击上传</div>
      </div>
    </div>
  </Upload>
</template>

<style lang="less" scoped>
.upload-wrapper {
  width: 128px;
  height: 128px;
  border: 1px dashed #d9d9d9;
  border-radius: 4px;
  cursor: pointer;
  transition: border-color 0.3s;
  display: flex;
  justify-content: center;
  align-items: center;

  &:hover {
    border-color: #1890ff;
  }

  .avatar {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .upload-placeholder {
    text-align: center;
    
    .anticon {
      font-size: 24px;
      color: #999;
    }

    .upload-text {
      margin-top: 8px;
      color: #999;
    }
  }
}
</style> 